Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ノートの下書き(draft of note) #15298

Open
wants to merge 56 commits into
base: develop
Choose a base branch
from

Conversation

tai-cha
Copy link
Contributor

@tai-cha tai-cha commented Jan 16, 2025

What

ノートの下書きを実装する
・バックエンドを持ち全端末で同期可能に

draft

いまは離脱時に聞いてフッターで呼び戻す形になってる
image
image

Why

close #7098

Additional info (optional)

Checklist

  • Read the contribution guide
  • Test working in a local environment
  • (If needed) Add story of storybook
  • (If needed) Update CHANGELOG.md
  • (If possible) Add tests

Sorry, something went wrong.

@github-actions github-actions bot added packages/backend Server side specific issue/PR packages/misskey-js labels Jan 16, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 16, 2025

まだバックエンドだけ

Copy link

codecov bot commented Jan 16, 2025

Codecov Report

Attention: Patch coverage is 57.47180% with 905 lines in your changes missing coverage. Please review.

Project coverage is 40.54%. Comparing base (a3d236c) to head (1443263).
Report is 560 commits behind head on develop.

Files with missing lines Patch % Lines
...rontend/src/components/MkNoteDraftSelectDialog.vue 0.00% 237 Missing and 1 partial ⚠️
...nd/src/server/api/endpoints/notes/drafts/update.ts 52.78% 178 Missing ⚠️
...nd/src/server/api/endpoints/notes/drafts/create.ts 52.47% 173 Missing ⚠️
...ackend/src/core/entities/NoteDraftEntityService.ts 31.07% 122 Missing ⚠️
packages/backend/src/core/NoteDraftService.ts 34.24% 96 Missing ⚠️
packages/frontend/src/scripts/get-note-summary.ts 3.12% 31 Missing ⚠️
...kend/src/server/api/endpoints/notes/drafts/list.ts 71.21% 19 Missing ⚠️
packages/frontend/src/pages/admin/roles.editor.vue 0.00% 19 Missing ⚠️
...nd/src/server/api/endpoints/notes/drafts/delete.ts 72.13% 17 Missing ⚠️
packages/frontend/src/pages/admin/roles.vue 0.00% 7 Missing ⚠️
... and 1 more
Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #15298      +/-   ##
===========================================
- Coverage    41.74%   40.54%   -1.21%     
===========================================
  Files         1549     1616      +67     
  Lines       196555   212177   +15622     
  Branches      2767     4063    +1296     
===========================================
+ Hits         82055    86030    +3975     
- Misses      113939   125508   +11569     
- Partials       561      639      +78     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

github-actions bot commented Jan 16, 2025

このPRによるapi.jsonの差分

差分はこちら
--- base
+++ head
@@ -60013,6 +60013,1236 @@
         }
       }
     },
+    "/notes/drafts": {
+      "post": {
+        "operationId": "notes___drafts",
+        "summary": "notes/drafts",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *read:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "limit": {
+                    "type": "integer",
+                    "minimum": 1,
+                    "maximum": 100,
+                    "default": 30
+                  },
+                  "sinceId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  },
+                  "untilId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  }
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK (with results)",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "type": "object",
+                    "$ref": "#/components/schemas/NoteDraft"
+                  }
+                }
+              }
+            }
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "INVALID_PARAM": {
+                    "value": {
+                      "error": {
+                        "message": "Invalid param.",
+                        "code": "INVALID_PARAM",
+                        "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Authentication error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "CREDENTIAL_REQUIRED": {
+                    "value": {
+                      "error": {
+                        "message": "Credential required.",
+                        "code": "CREDENTIAL_REQUIRED",
+                        "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "403": {
+            "description": "Forbidden error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "AUTHENTICATION_FAILED": {
+                    "value": {
+                      "error": {
+                        "message": "Authentication failed. Please ensure your token is correct.",
+                        "code": "AUTHENTICATION_FAILED",
+                        "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "418": {
+            "description": "I'm Ai",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "I_AM_AI": {
+                    "value": {
+                      "error": {
+                        "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+                        "code": "I_AM_AI",
+                        "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "500": {
+            "description": "Internal server error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "INTERNAL_ERROR": {
+                    "value": {
+                      "error": {
+                        "message": "Internal error occurred. Please contact us if the error persists.",
+                        "code": "INTERNAL_ERROR",
+                        "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/notes/drafts/create": {
+      "post": {
+        "operationId": "notes___drafts___create",
+        "summary": "notes/drafts/create",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts/create.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "visibility": {
+                    "type": "string",
+                    "enum": [
+                      "public",
+                      "home",
+                      "followers",
+                      "specified"
+                    ],
+                    "default": "public"
+                  },
+                  "visibleUserIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "cw": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 1,
+                    "maxLength": 100
+                  },
+                  "hashtag": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "maxLength": 200
+                  },
+                  "localOnly": {
+                    "type": "boolean",
+                    "default": false
+                  },
+                  "reactionAcceptance": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "enum": [
+                      null,
+                      "likeOnly",
+                      "likeOnlyForRemote",
+                      "nonSensitiveOnly",
+                      "nonSensitiveOnlyForLocalLikeOnlyForRemote"
+                    ],
+                    "default": null
+                  },
+                  "replyId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "renoteId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "channelId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "text": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 0,
+                    "maxLength": 3000
+                  },
+                  "fileIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "minItems": 1,
+                    "maxItems": 16,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "poll": {
+                    "type": [
+                      "object",
+                      "null"
+                    ],
+                    "properties": {
+                      "choices": {
+                        "type": "array",
+                        "uniqueItems": true,
+                        "minItems": 0,
+                        "maxItems": 10,
+                        "items": {
+                          "type": "string",
+                          "minLength": 1,
+                          "maxLength": 50
+                        }
+                      },
+                      "multiple": {
+                        "type": "boolean"
+                      },
+                      "expiresAt": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ]
+                      },
+                      "expiredAfter": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ],
+                        "minimum": 1
+                      }
+                    },
+                    "required": [
+                      "choices"
+                    ]
+                  }
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK (with results)",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "object",
+                  "properties": {
+                    "createdDraft": {
+                      "type": "object",
+                      "$ref": "#/components/schemas/NoteDraft"
+                    }
+                  },
+                  "required": [
+                    "createdDraft"
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "NO_SUCH_RENOTE_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such renote target.",
+                        "code": "NO_SUCH_RENOTE_TARGET",
+                        "id": "b5c90186-4ab0-49c8-9bba-a1f76c282ba4"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote a pure Renote.",
+                        "code": "CANNOT_RENOTE_TO_A_PURE_RENOTE",
+                        "id": "fd4cc33e-2a37-48dd-99cc-9b806eb2031a"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_DUE_TO_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote due to target visibility.",
+                        "code": "CANNOT_RENOTE_DUE_TO_VISIBILITY",
+                        "id": "be9529e9-fe72-4de0-ae43-0b363c4938af"
+                      }
+                    }
+                  },
+                  "NO_SUCH_REPLY_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such reply target.",
+                        "code": "NO_SUCH_REPLY_TARGET",
+                        "id": "749ee0f6-d3da-459a-bf02-282e2da4292c"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to an invisible Note.",
+                        "code": "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE",
+                        "id": "b98980fa-3780-406c-a935-b6d0eeee10d1"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not reply to a pure Renote.",
+                        "code": "CANNOT_REPLY_TO_A_PURE_RENOTE",
+                        "id": "3ac74a84-8fd5-4bb0-870f-01804f82ce15"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to a specified visibility note with extended visibility.",
+                        "code": "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY",
+                        "id": "ed940410-535c-4d5e-bfa3-af798671e93c"
+                      }
+                    }
+                  },
+                  "CANNOT_CREATE_ALREADY_EXPIRED_POLL": {
+                    "value": {
+                      "error": {
+                        "message": "Poll is already expired.",
+                        "code": "CANNOT_CREATE_ALREADY_EXPIRED_POLL",
+                        "id": "04da457d-b083-4055-9082-955525eda5a5"
+                      }
+                    }
+                  },
+                  "NO_SUCH_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "No such channel.",
+                        "code": "NO_SUCH_CHANNEL",
+                        "id": "b1653923-5453-4edc-b786-7c4f39bb0bbb"
+                      }
+                    }
+                  },
+                  "YOU_HAVE_BEEN_BLOCKED": {
+                    "value": {
+                      "error": {
+                        "message": "You have been blocked by this user.",
+                        "code": "YOU_HAVE_BEEN_BLOCKED",
+                        "id": "b390d7e1-8a5e-46ed-b625-06271cafd3d3"
+                      }
+                    }
+                  },
+                  "NO_SUCH_FILE": {
+                    "value": {
+                      "error": {
+                        "message": "Some files are not found.",
+                        "code": "NO_SUCH_FILE",
+                        "id": "b6992544-63e7-67f0-fa7f-32444b1b5306"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot renote outside of channel.",
+                        "code": "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL",
+                        "id": "33510210-8452-094c-6227-4a6c05d99f00"
+                      }
+                    }
+                  },
+                  "CONTAINS_PROHIBITED_WORDS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it contains prohibited words.",
+                        "code": "CONTAINS_PROHIBITED_WORDS",
+                        "id": "aa6e01d3-a85c-669d-758a-76aab43af334"
+                      }
+                    }
+                  },
+                  "CONTAINS_TOO_MANY_MENTIONS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it exceeds the allowed number of mentions.",
+                        "code": "CONTAINS_TOO_MANY_MENTIONS",
+                        "id": "4de0363a-3046-481b-9b0f-feff3e211025"
+                      }
+                    }
+                  },
+                  "TOO_MANY_DRAFTS": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot create drafts any more.",
+                        "code": "TOO_MANY_DRAFTS",
+                        "id": "9ee33bbe-fde3-4c71-9b51-e50492c6b9c8"
+                      }
+                    }
+                  },
+                  "INVALID_PARAM": {
+                    "value": {
+                      "error": {
+                        "message": "Invalid param.",
+                        "code": "INVALID_PARAM",
+                        "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Authentication error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "CREDENTIAL_REQUIRED": {
+                    "value": {
+                      "error": {
+                        "message": "Credential required.",
+                        "code": "CREDENTIAL_REQUIRED",
+                        "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "403": {
+            "description": "Forbidden error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "AUTHENTICATION_FAILED": {
+                    "value": {
+                      "error": {
+                        "message": "Authentication failed. Please ensure your token is correct.",
+                        "code": "AUTHENTICATION_FAILED",
+                        "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "418": {
+            "description": "I'm Ai",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "I_AM_AI": {
+                    "value": {
+                      "error": {
+                        "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+                        "code": "I_AM_AI",
+                        "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "429": {
+            "description": "Too many requests",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "RATE_LIMIT_EXCEEDED": {
+                    "value": {
+                      "error": {
+                        "message": "Rate limit exceeded. Please try again later.",
+                        "code": "RATE_LIMIT_EXCEEDED",
+                        "id": "d5826d14-3982-4d2e-8011-b9e9f02499ef"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "500": {
+            "description": "Internal server error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "INTERNAL_ERROR": {
+                    "value": {
+                      "error": {
+                        "message": "Internal error occurred. Please contact us if the error persists.",
+                        "code": "INTERNAL_ERROR",
+                        "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/notes/drafts/delete": {
+      "post": {
+        "operationId": "notes___drafts___delete",
+        "summary": "notes/drafts/delete",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts/delete.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "draftId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  }
+                },
+                "required": [
+                  "draftId"
+                ]
+              }
+            }
+          }
+        },
+        "responses": {
+          "204": {
+            "description": "OK (without any results)"
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "NO_SUCH_NOTE_DRAFT": {
+                    "value": {
+                      "error": {
+                        "message": "No such note draft.",
+                        "code": "NO_SUCH_NOTE_DRAFT",
+                        "id": "49cd6b9d-848e-41ee-b0b9-adaca711a6b1"
+                      }
+                    }
+                  },
+                  "ACCESS_DENIED": {
+                    "value": {
+                      "error": {
+                        "message": "Access denied.",
+                        "code": "ACCESS_DENIED",
+                        "id": "56f35758-7dd5-468b-8439-5d6fb8ec9b8e"
+                      }
+                    }
+                  },
+                  "INVALID_PARAM": {
+                    "value": {
+                      "error": {
+                        "message": "Invalid param.",
+                        "code": "INVALID_PARAM",
+                        "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Authentication error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "CREDENTIAL_REQUIRED": {
+                    "value": {
+                      "error": {
+                        "message": "Credential required.",
+                        "code": "CREDENTIAL_REQUIRED",
+                        "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "403": {
+            "description": "Forbidden error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "AUTHENTICATION_FAILED": {
+                    "value": {
+                      "error": {
+                        "message": "Authentication failed. Please ensure your token is correct.",
+                        "code": "AUTHENTICATION_FAILED",
+                        "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "418": {
+            "description": "I'm Ai",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "I_AM_AI": {
+                    "value": {
+                      "error": {
+                        "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+                        "code": "I_AM_AI",
+                        "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "500": {
+            "description": "Internal server error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "INTERNAL_ERROR": {
+                    "value": {
+                      "error": {
+                        "message": "Internal error occurred. Please contact us if the error persists.",
+                        "code": "INTERNAL_ERROR",
+                        "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/notes/drafts/update": {
+      "post": {
+        "operationId": "notes___drafts___update",
+        "summary": "notes/drafts/update",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts/update.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "draftId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  },
+                  "visibility": {
+                    "type": "string",
+                    "enum": [
+                      "public",
+                      "home",
+                      "followers",
+                      "specified"
+                    ],
+                    "default": "public"
+                  },
+                  "visibleUserIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "cw": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 1,
+                    "maxLength": 100
+                  },
+                  "hashtag": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "maxLength": 200
+                  },
+                  "localOnly": {
+                    "type": "boolean",
+                    "default": false
+                  },
+                  "reactionAcceptance": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "enum": [
+                      null,
+                      "likeOnly",
+                      "likeOnlyForRemote",
+                      "nonSensitiveOnly",
+                      "nonSensitiveOnlyForLocalLikeOnlyForRemote"
+                    ],
+                    "default": null
+                  },
+                  "replyId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "renoteId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "channelId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "text": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 0,
+                    "maxLength": 3000
+                  },
+                  "fileIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "minItems": 1,
+                    "maxItems": 16,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "poll": {
+                    "type": [
+                      "object",
+                      "null"
+                    ],
+                    "properties": {
+                      "choices": {
+                        "type": "array",
+                        "uniqueItems": true,
+                        "minItems": 0,
+                        "maxItems": 10,
+                        "items": {
+                          "type": "string",
+                          "minLength": 1,
+                          "maxLength": 50
+                        }
+                      },
+                      "multiple": {
+                        "type": "boolean"
+                      },
+                      "expiresAt": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ]
+                      },
+                      "expiredAfter": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ],
+                        "minimum": 1
+                      }
+                    },
+                    "required": [
+                      "choices"
+                    ]
+                  }
+                },
+                "required": [
+                  "draftId"
+                ]
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK (with results)",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "object",
+                  "properties": {
+                    "updatedDraft": {
+                      "type": "object",
+                      "$ref": "#/components/schemas/NoteDraft"
+                    }
+                  },
+                  "required": [
+                    "updatedDraft"
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "NO_SUCH_RENOTE_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such renote target.",
+                        "code": "NO_SUCH_RENOTE_TARGET",
+                        "id": "b5c90186-4ab0-49c8-9bba-a1f76c282ba4"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote a pure Renote.",
+                        "code": "CANNOT_RENOTE_TO_A_PURE_RENOTE",
+                        "id": "fd4cc33e-2a37-48dd-99cc-9b806eb2031a"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_DUE_TO_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote due to target visibility.",
+                        "code": "CANNOT_RENOTE_DUE_TO_VISIBILITY",
+                        "id": "be9529e9-fe72-4de0-ae43-0b363c4938af"
+                      }
+                    }
+                  },
+                  "NO_SUCH_REPLY_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such reply target.",
+                        "code": "NO_SUCH_REPLY_TARGET",
+                        "id": "749ee0f6-d3da-459a-bf02-282e2da4292c"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to an invisible Note.",
+                        "code": "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE",
+                        "id": "b98980fa-3780-406c-a935-b6d0eeee10d1"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not reply to a pure Renote.",
+                        "code": "CANNOT_REPLY_TO_A_PURE_RENOTE",
+                        "id": "3ac74a84-8fd5-4bb0-870f-01804f82ce15"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to a specified visibility note with extended visibility.",
+                        "code": "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY",
+                        "id": "ed940410-535c-4d5e-bfa3-af798671e93c"
+                      }
+                    }
+                  },
+                  "CANNOT_CREATE_ALREADY_EXPIRED_POLL": {
+                    "value": {
+                      "error": {
+                        "message": "Poll is already expired.",
+                        "code": "CANNOT_CREATE_ALREADY_EXPIRED_POLL",
+                        "id": "04da457d-b083-4055-9082-955525eda5a5"
+                      }
+                    }
+                  },
+                  "NO_SUCH_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "No such channel.",
+                        "code": "NO_SUCH_CHANNEL",
+                        "id": "b1653923-5453-4edc-b786-7c4f39bb0bbb"
+                      }
+                    }
+                  },
+                  "YOU_HAVE_BEEN_BLOCKED": {
+                    "value": {
+                      "error": {
+                        "message": "You have been blocked by this user.",
+                        "code": "YOU_HAVE_BEEN_BLOCKED",
+                        "id": "b390d7e1-8a5e-46ed-b625-06271cafd3d3"
+                      }
+                    }
+                  },
+                  "NO_SUCH_FILE": {
+                    "value": {
+                      "error": {
+                        "message": "Some files are not found.",
+                        "code": "NO_SUCH_FILE",
+                        "id": "b6992544-63e7-67f0-fa7f-32444b1b5306"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot renote outside of channel.",
+                        "code": "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL",
+                        "id": "33510210-8452-094c-6227-4a6c05d99f00"
+                      }
+                    }
+                  },
+                  "CONTAINS_PROHIBITED_WORDS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it contains prohibited words.",
+                        "code": "CONTAINS_PROHIBITED_WORDS",
+                        "id": "aa6e01d3-a85c-669d-758a-76aab43af334"
+                      }
+                    }
+                  },
+                  "CONTAINS_TOO_MANY_MENTIONS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it exceeds the allowed number of mentions.",
+                        "code": "CONTAINS_TOO_MANY_MENTIONS",
+                        "id": "4de0363a-3046-481b-9b0f-feff3e211025"
+                      }
+                    }
+                  },
+                  "NO_SUCH_NOTE_DRAFT": {
+                    "value": {
+                      "error": {
+                        "message": "No such note draft.",
+                        "code": "NO_SUCH_NOTE_DRAFT",
+                        "id": "49cd6b9d-848e-41ee-b0b9-adaca711a6b1"
+                      }
+                    }
+                  },
+                  "ACCESS_DENIED": {
+                    "value": {
+                      "error": {
+                        "message": "Access denied.",
+                        "code": "ACCESS_DENIED",
+                        "id": "56f35758-7dd5-468b-8439-5d6fb8ec9b8e"
+                      }
+                    }
+                  },
+                  "INVALID_PARAM": {
+                    "value": {
+                      "error": {
+                        "message": "Invalid param.",
+                        "code": "INVALID_PARAM",
+                        "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Authentication error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "CREDENTIAL_REQUIRED": {
+                    "value": {
+                      "error": {
+                        "message": "Credential required.",
+                        "code": "CREDENTIAL_REQUIRED",
+                        "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "403": {
+            "description": "Forbidden error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "AUTHENTICATION_FAILED": {
+                    "value": {
+                      "error": {
+                        "message": "Authentication failed. Please ensure your token is correct.",
+                        "code": "AUTHENTICATION_FAILED",
+                        "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "418": {
+            "description": "I'm Ai",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "I_AM_AI": {
+                    "value": {
+                      "error": {
+                        "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+                        "code": "I_AM_AI",
+                        "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "429": {
+            "description": "Too many requests",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "RATE_LIMIT_EXCEEDED": {
+                    "value": {
+                      "error": {
+                        "message": "Rate limit exceeded. Please try again later.",
+                        "code": "RATE_LIMIT_EXCEEDED",
+                        "id": "d5826d14-3982-4d2e-8011-b9e9f02499ef"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "500": {
+            "description": "Internal server error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "INTERNAL_ERROR": {
+                    "value": {
+                      "error": {
+                        "message": "Internal error occurred. Please contact us if the error persists.",
+                        "code": "INTERNAL_ERROR",
+                        "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
     "/notes/favorites/create": {
       "post": {
         "operationId": "notes___favorites___create",
@@ -80044,6 +81274,215 @@
           "repliesCount"
         ]
       },
+      "NoteDraft": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "createdAt": {
+            "type": "string",
+            "format": "date-time"
+          },
+          "text": {
+            "type": [
+              "string",
+              "null"
+            ]
+          },
+          "cw": {
+            "type": [
+              "string",
+              "null"
+            ]
+          },
+          "userId": {
+            "type": "string",
+            "format": "id"
+          },
+          "user": {
+            "type": "object",
+            "$ref": "#/components/schemas/UserLite"
+          },
+          "replyId": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "renoteId": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "reply": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/Note"
+              }
+            ]
+          },
+          "renote": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/Note"
+              }
+            ]
+          },
+          "visibility": {
+            "type": "string",
+            "enum": [
+              "public",
+              "home",
+              "followers",
+              "specified"
+            ]
+          },
+          "visibleUserIds": {
+            "type": "array",
+            "items": {
+              "type": "string",
+              "format": "id"
+            }
+          },
+          "fileIds": {
+            "type": "array",
+            "items": {
+              "type": "string",
+              "format": "id"
+            }
+          },
+          "files": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "$ref": "#/components/schemas/DriveFile"
+            }
+          },
+          "hashtag": {
+            "type": "string"
+          },
+          "poll": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "properties": {
+              "expiresAt": {
+                "type": [
+                  "string",
+                  "null"
+                ],
+                "format": "date-time"
+              },
+              "expiredAfter": {
+                "type": [
+                  "number",
+                  "null"
+                ]
+              },
+              "multiple": {
+                "type": "boolean"
+              },
+              "choices": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              }
+            },
+            "required": [
+              "multiple",
+              "choices"
+            ]
+          },
+          "channelId": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "channel": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "properties": {
+              "id": {
+                "type": "string"
+              },
+              "name": {
+                "type": "string"
+              },
+              "color": {
+                "type": "string"
+              },
+              "isSensitive": {
+                "type": "boolean"
+              },
+              "allowRenoteToExternal": {
+                "type": "boolean"
+              },
+              "userId": {
+                "type": [
+                  "string",
+                  "null"
+                ]
+              }
+            },
+            "required": [
+              "id",
+              "name",
+              "color",
+              "isSensitive",
+              "allowRenoteToExternal",
+              "userId"
+            ]
+          },
+          "localOnly": {
+            "type": "boolean"
+          },
+          "reactionAcceptance": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "enum": [
+              "likeOnly",
+              "likeOnlyForRemote",
+              "nonSensitiveOnly",
+              "nonSensitiveOnlyForLocalLikeOnlyForRemote",
+              null
+            ]
+          }
+        },
+        "required": [
+          "id",
+          "createdAt",
+          "text",
+          "userId",
+          "user",
+          "visibility",
+          "reactionAcceptance"
+        ]
+      },
       "NoteReaction": {
         "type": "object",
         "properties": {
@@ -82753,6 +84192,9 @@
           },
           "canImportUserLists": {
             "type": "boolean"
+          },
+          "noteDraftLimit": {
+            "type": "integer"
           }
         },
         "required": [
@@ -82786,7 +84228,8 @@
           "canImportBlocking",
           "canImportFollowing",
           "canImportMuting",
-          "canImportUserLists"
+          "canImportUserLists",
+          "noteDraftLimit"
         ]
       },
       "ReversiGameLite": {

Get diff files from Workflow Page

@kakkokari-gtyih
Copy link
Contributor

kakkokari-gtyih commented Jan 16, 2025

今日は忙しいので明日以降に見ます

tai-cha and others added 2 commits January 16, 2025 20:00
Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 16, 2025

まだDraftのためレビュー頂いても変わる可能性はままある

@github-actions github-actions bot added the packages/frontend Client side specific issue/PR label Jan 16, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 16, 2025

とりあえず下書きの取得、保存、削除はかけたのでどのタイミングで保存するかとかUIの調整とかはやっていきたい

@kakkokari-gtyih
Copy link
Contributor

ちなみに、内容がある状態で下書き画面開いて選択した場合って問答無用で上書きされちゃいます…? (過去の分を消そうとして誤選択とか起きそうな気がしたので)

下書きの復旧にワンクッションある(復旧したい下書きを選択してから決定ボタンを押す)ので誤爆は稀な気がする

@kakkokari-gtyih
Copy link
Contributor

@tai-cha 一通り見たので確認お願いします

@kakkokari-gtyih kakkokari-gtyih removed their assignment Jan 17, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 17, 2025

下書き自体セレクタはまあいい感じとして下書きの挙動がこれでいいかはしゅいろさんの判断になりそうだからいったんこの仕様で細かいFixいれるか(リクエストパラメータ周りなど)

@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 17, 2025

良さそうに見えるけどコード書いたの我々だしレビューに進めるか

@tai-cha tai-cha marked this pull request as ready for review January 17, 2025 05:33
@tai-cha tai-cha changed the title feat: draft of note feat: ノートの下書き(draft of note) Jan 17, 2025
@kakkokari-gtyih kakkokari-gtyih added this to the v2025.2.0~ milestone Jan 17, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 19, 2025

CHANGELOG忘れてるけど次バージョン以降になる予定だから変な位置にCHANGELOG行きそうだから保留

@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 20, 2025

エンドポイント定義の方法が変わったため、コンフリクト解消

@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 28, 2025

CHANGELOG追加

noridev added a commit to kokonect-link/cherrypick that referenced this pull request Feb 6, 2025
  - 작성 중인 노트를 서버 또는 기기에 저장할 수 있습니다.
  - 서버에 저장된 초안은 모든 기기에서 동기화 됩니다.
Copy link
Contributor

@1673beta 1673beta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご確認いただけたら幸いです

}
} else {
return { canClosePostForm: true };
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この機能がマージされたサーバーの利用者の意見: 毎回「サーバーに保存しますか?」というのは意外と体験の悪化になるので「保存する(二度と表示しない)」「保存しない(二度と表示しない)」が欲しい

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これをする場合、UI的には、ボタンが増えるよりも二度と表示しないかどうかのチェックボックスが追加で増えるような形が望ましい気もします(現在のフロントエンド的に実現出来るかは存じませんが)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
packages/backend Server side specific issue/PR packages/frontend Client side specific issue/PR packages/misskey-js
Projects
Development

Successfully merging this pull request may close these issues.

Draft of note
4 participants